package steveshrader.budget.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import steveshrader.budget.client.BudgetService;
import steveshrader.budget.server.model.Expense;
import steveshrader.budget.server.model.PMF;
import steveshrader.budget.server.model.Vendor;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class BudgetServiceImpl extends RemoteServiceServlet implements BudgetService {
		@Override
		protected void checkPermutationStrongName() throws SecurityException {
			return;
		}
		
        /**
         * all services require a User to be authenticated...this will return the id for
         * the authenticated User or null if the user has not logged in.
         */
        private String getUserId() {
                String userId = null;
                UserService userService = UserServiceFactory.getUserService();
                User currentUser = userService.getCurrentUser();
                if(currentUser != null) {
                        userId = currentUser.getEmail();
                }
                return userId;
        }
       
        /**
         * Store an Expense in the DB
         */
        private String addExpense(String userId, steveshrader.budget.client.dto.Expense expense) {
                Expense newExpense = new Expense(userId, expense.getDate(), expense.getAmount(), expense.getVendor(), expense.getExpenseType(), expense.getPaymentType());
                Vendor newVendor;
                       
                PersistenceManager pm = PMF.get().getPersistenceManager();
                try {
                        //store the new expense
                        pm.makePersistent(newExpense);
                               
                        //update the existing Vendor if found...not found will go to the catch block for JDOObjectNotFoundException
                        Query query = pm.newQuery(Vendor.class);
                        query.setFilter("userId == userIdParam && name == nameParam");
                        query.declareParameters("String userIdParam, String nameParam");
                        query.setUnique(true);
                        newVendor = (Vendor) query.execute(userId, expense.getVendor());
                        if(newVendor == null) { //this is a new vendor so add it
                                newVendor = new Vendor(userId, expense.getVendor(), expense.getExpenseType(), expense.getPaymentType());
                                pm.makePersistent(newVendor);
                        }
                        else {  //the vendor already exists so just update it.
                                newVendor.setLastExpenseType(expense.getExpenseType());
                                newVendor.setLastPaymentType(expense.getPaymentType());
                        }
                } finally {
                        pm.close();
                }
                       
                return "Expense Added";
        }

        /**
         * Publicly available interface to adding an Expense.  Securely retrieves the
         * current user and if found will then add the expense to the DB
         */
        public String addExpense(steveshrader.budget.client.dto.Expense expense) {
                String userId = getUserId();
                if(userId == null) {
                        return "User is not Logged In";
                }
                else {
                        return addExpense(userId, expense);
                }                      
        }
       
        /**
         * Publicly available interface to adding an Expense.  Securely retrieves the
         * current user and if found will then add the expense to the DB
         */
        public String addExpenses(List<steveshrader.budget.client.dto.Expense> expenses) {
                String userId = getUserId();
                if(userId == null) {
                        return "User is not Logged In";
                }
                else {
                        for(steveshrader.budget.client.dto.Expense e : expenses) {
                                addExpense(userId, e);
                        }
                        return "Expenses Added";
                }                      
        }
       
        /**
         * Remove an Expense from the DB
         */
        public String deleteExpense(Long expenseId) {
                String userId = getUserId();
                if(userId != null) {
                        PersistenceManager pm = PMF.get().getPersistenceManager();
                        try {
                                //get the existing expense
                                Expense e = pm.getObjectById(Expense.class, expenseId);
                                pm.deletePersistent(e);
                        } finally {
                                pm.close();
                        }
                        return "Expense Deleted";
                }
                else {
                        return "User is not Logged In";
                }
        }

        /**
         * Get a list of Vendors previously used
         */
        @SuppressWarnings("unchecked")  //query is against Vendor.class so it will return List<Vendor>
        public List<steveshrader.budget.client.dto.Vendor> getVendors() {
                List<steveshrader.budget.client.dto.Vendor> dtoVendors = new ArrayList<steveshrader.budget.client.dto.Vendor>();
                String userId = getUserId();
                if(userId != null) {    
                        PersistenceManager pm = PMF.get().getPersistenceManager();
                        Query query = pm.newQuery(Vendor.class);
                        query.setFilter("userId == userIdParam");
                        query.declareParameters("String userIdParam");
                        List<Vendor> vendors = (List<Vendor>) query.execute(userId);
                       
                        for(Vendor v : vendors) {
                                dtoVendors.add(new steveshrader.budget.client.dto.Vendor(v.getName(), v.getLastExpenseType(), v.getLastPaymentType()));
                        }
                       
                        pm.close();    
                }
                return dtoVendors;              
        }
       
        /**
         * Retrieve a list of Expense depending on the Dates and Vendor passed in.  There will be two
         * execution paths because Vendor does not need to be passed in as a filter.
         */
        @SuppressWarnings("unchecked")  //query is against Expense.class so it will return List<Expense>
        public List<steveshrader.budget.client.dto.Expense> getExpenses(Date startDate, Date endDate, String vendor) {
                List<steveshrader.budget.client.dto.Expense> dtoExpenses = new ArrayList<steveshrader.budget.client.dto.Expense>();
                String userId = getUserId();
                if(userId != null) {
                        List<Expense> expenses = null;
                       
                        PersistenceManager pm = PMF.get().getPersistenceManager();
                        Query query = pm.newQuery(Expense.class);
                        query.declareImports("import java.util.Date");
                        query.setOrdering("date asc");
                       
                        String queryFilter = "userId == userIdParam && date >= :startDateParam && date <= :endDateParam";
                       
                        @SuppressWarnings("rawtypes")
						Map queryParameters = new HashMap();
                        queryParameters.put("userIdParam", userId);
                        queryParameters.put("startDateParam", startDate);
                        queryParameters.put("endDateParam", endDate);
                       
                        if(vendor != null && vendor.trim().length() > 0) {      //if a vendor passed in
                                queryFilter = queryFilter + " && vendor == vendorParam";
                                queryParameters.put("vendorParam", vendor);
                        }
                       
                        query.setFilter(queryFilter);
                        expenses = (List<Expense>) query.executeWithMap(queryParameters);
                       
                        for(Expense e : expenses) {	//userId and vendor filter not working on query...don't know why but do it here.
                        	if(userId.equals(e.getUserId())) {
                        		if(vendor == null || vendor.length() == 0 || vendor.equals(e.getVendor())) {
                        			dtoExpenses.add(new steveshrader.budget.client.dto.Expense(e.getId(), e.getDate(), e.getAmount(), e.getVendor(), e.getExpenseType(), e.getPaymentType()));
                        		}
                        	}
                        }
                       
                        pm.close();
                }
                return dtoExpenses;
        }
}